<?php
// +-------------------------------------------------------------------------+
// | PHP Version 5                                                           |
// +-------------------------------------------------------------------------+
// | ImaEngine - web development framework                                   |
// | Copyright (c) 2006-2007 Sardar Yumatov <ja.doma@gmail.com>              |
// +-------------------------------------------------------------------------+
// | This library is free software; you can redistribute it and/or modify it |
// | under the terms of the GNU Lesser General Public License as published   |
// | by the Free Software Foundation; either version 2.1 of the License, or  |
// | (at your option) any later version.                                     |
// |                                                                         |
// | This library is distributed in the hope that it will be useful, but     |
// | WITHOUT ANY WARRANTY; without even the implied warranty of              |
// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                    |
// | See the GNU Lesser General Public License for more details.             |
// |                                                                         |
// | You should have received a copy of the GNU Lesser General Public        |
// | License along with this library; if not, write to the Free Software     |
// | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
// +-------------------------------------------------------------------------+

/**
* This file defines interface nedded for text-stream processors.
*
* @package common.view
* @category text-stream
* @version v1.0
* @author Sardar Yumatov <ja.doma@gmail.com>
* @copyright Copyright &copy; 2006-2007 Sardar Yumatov
*
* @PHP5only
*/

/**
 * Defines an object (model) renderable in the text-stream environment.
 * The text-stream is a simple event driven text generation model.
 * @author Sardar Yumatov <ja.doma@gmail.com>
 */
interface TextStreamRenderable {

	/**
	 * Returns text stream representation of specific $type.
	 * Depending on the $type a special render will be created. The type may be
	 * 'text', 'html', 'xhtml' or alike. If type is not supported, then
	 * exception will be thrown.
	 * @throws UnsupportedException if specific $type is not supported
	 * @param string $type a specific render type
	 * @return AbstractTextStreamView the view object
	 */
	public function getTextStreamView($type);

	/**
	 * Returns list of supported renders, accepted by <tt>getTextStreamView()</tt>.
	 * This method is probably for debugging only.
	 * @return array of string supported render type names as [type=>type]
	 */
	public function getSupportedStreamViewTypes();
}


/**
 * Base class for all view objects implementing text-stream based renders.
 *
 * All view objects are organized in tree. Each child gets a "window" (event) where content
 * should be placed. It's up to the child how to produce the content and which events to
 * generate for its children (propagating event).
 *
 * By starting output from the root a full text/html document can be created. Each view
 * object may use anything to render its content, usually a text templater is used.
 *
 * New text-stream view is always a single (root) node until it is added to the tree.
 *
 * @author Sardar Yumatov <ja.doma@gmail.com>
 */
abstract class AbstractTextStreamView {

	/**
	 * All child views of this view.
	 * @var array of TextStreamView
	 */
	protected $children = array();

	/**
	 * Parent view, null for root view.
	 * @var AbstractTextStreamView
	 */
	protected $parent = null;

	/**
	 * Set new parent view.
	 * This method will be called by <tt>addView()</tt> when adding this view to given
	 * parent $view. Method may throw exception if such action is not allowed.
	 * @throws RuntimeException if this view doesn't accept given parent $view
	 * @param AbstractTextStreamView $view the parent view to set, null if this view should become (temporary) the root view
	 * @return void
	 */
	protected function setParentView($view) {
		$this->parent = $view;
	}

	/**
	 * Returns parent view.
	 * <b>Warning:</b> the view object may not change parent view without notifying
	 * old parent. Usually there is no need to change the parent during request.
	 * @return AbstractTextStreamView
	 */
	public function getParentView() {
		return $this->parent;
	}

	/**
	 * Add new child $view to the tree.
	 * The child will change its parent to $this view. If parent of given $view is already
	 * $this view, then nothign will be done.
	 * @param AbstractTextStreamView $view the view object
	 * @return void
	 */
	public function addView(AbstractTextStreamView $view) {
		if($view === $this) throw new InvalidArgumentException("Can't add itself!");
		if(!is_null($par = $view->getParentView())) {
			if($par === $this) return;
			$par->removeView($view);
		}
		$view->setParentView($this); //throws RuntimeException if not allowed
		$this->children[] = $view;
	}

	/**
	 * Remove given view if exists.
	 * Used when given $view should be moved to the new place in the view tree.
	 * This method removes given $view from the children and sets parent to null.
	 * If parent of given $view is not $this view, then nothign will be done.
	 * @throws LogicException if given $view is not the child of this view
	 * @param AbstractTextStreamView $view child view to remove from childrens of this view
	 * @return void
	 */
	protected function removeView(AbstractTextStreamView $view) {
		if(is_null($par = $view->getParentView())) return;
		elseif($par != $this) throw new LogicException("The parent of given view object is not null and is ot this view, software bug found!");
		foreach ($this->children as $k=>$child) if($child === $view) {
			unset($this->children[$k]);
			break;
		}
		$view->setParentView(null); //may throw RuntimeException
	}

	/**
	 * Generate output for specific label.
	 *
	 * Each view object may have any number of labels, usually defined in the template. Such label
	 * defines the place where child module may place the contents. It is up to the view how to handle
	 * this event, it doesn't need to be propagated to the childs.
	 *
	 * Only the root view may expect null as $label, all other views should expect some properly documented
	 * labels/events from the parent view.
	 *
	 * @see outputAtMarker()
	 * @param string $label label defined in the template, null for the root view
	 * @return void
	 */
	public abstract function processOutput($label);

	/**
	 * Inform children about the $label being currently processed.
	 * This method simply propagates <tt>processOutput()</tt> call to all child views.
	 * @see processOutput()
	 * @param string $label label name, usually defined in the template
	 * @return void
	 */
	public function triggerProcessOutput($label) {
		foreach ($this->children as $child) $child->processOutput($label);
	}

	/**
	 * Returns list of labels recognized by this object.
	 * Usually a single view object accepts only one label ('content' for example).
	 * By default method returns empty list.
	 * This info may help advanced engine maintenance tools to create proper templates.
	 * @return array of string - labels as [label => label]
	 */
	public function getExpectingLabels() {
		return array();
	}

	/**
	 * Genrate output for specific marker.
	 *
	 * This method works like <tt>processOtutput()</tt> but may be triggered by event generated
	 * by any of the ancestor views. Logically it is intended to allow sub-modules insert some
	 * small metada necesssary for the main content to work. Usually top level view generates marker
	 * events when HTML header is being processed, to allow other modules insert CSS and JS resources.
	 * By default method does nothing.
	 *
	 * @see processOutput()
	 * @param string $marker name of the marker, usually defined in the ancestor's template
	 * @return void
	 */
	public function outputAtMarker($marker) {}

	/**
	 * Inform self and all child views about the $marker.
	 * This method simply propagates <tt>outputAtMarker()</tt> call to this and all child views.
	 * @see outputAtMarker()
	 * @param string $marker name of the marker, usually defined in this template
	 * @return void
	 */
	public function triggerMarker($marker) {
		$this->outputAtMarker($marker);
		foreach ($this->children as $child) $child->triggerMarker($marker);
	}

	/**
	 * Returns all recognized stream markers.
	 * On any marker listed here this object will probably generate some output when <tt>outputAtMarker()</tt>
	 * is called. By default method returns empty list.
	 * This info may help advanced engine maintenance tools to create proper templates.
	 * @return array of string - markers as [marker => marker]
	 */
	public function getExpectingMarkers() {
		return array();
	}

	/**
	 * Collect all recognized markers from this view object and all child views.
	 * This method returns all markers recognized by this subtree.
	 * This info may help advanced engine maintenance tools to create proper templates.
	 * @return array of string - markers as [marker => marker]
	 */
	public function collectExpectingMarkers() {
		$ret = array();
		foreach($this->children as $child) $ret += $child->collectExpectingMarkers();
		return $this->getExpectingMarkers() + $ret;
	}
}


?>